/*
 * @(#)Start.java
 *
 *
 * ver 1.0 Sep 28, 2007 ggranum
 */
package org.testfire.agent;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import javax.swing.JOptionPane;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.realm.MemoryRealm;
import org.apache.catalina.startup.Embedded;
import org.apache.commons.cli.*;
import org.testfire.agent.util.AgentPropertiesDialog;
import org.testfire.support.PasswordDialog;

public class Start
{
    static final String runningPath = System.getProperty( "user.dir" );

    private Start() {}

    public static void main( String[] args ) throws IOException
    {
        System.out.println( "Loading properties" );
        AgentProperties agentProps = AgentProperties.getInstance();
        try
        {
            File f = new File
                ( System.getProperty( "user.dir" ) + File.separator
                    + "httpserver.properties" );
            agentProps.loadProps( f );
        }
        catch ( IOException e )
        {
            System.out.println( "Fatal Error" );
            e.printStackTrace();
            System.exit( -1 );
        }

        Options opts = new Options();
        opts.addOption( "?", false, "Print the help for the AgentRunner" );

        Option lcoalModeOpt = new Option( "l", false,
            "If the agent should be started in local mode" );
        lcoalModeOpt.setRequired( false );
        opts.addOption( lcoalModeOpt );

        Option user = new Option( "u", true,
            "The username of the user this Agent is owned by. If blank you will be prompted for same." );
        user.setRequired( false );
        opts.addOption( user );

        Option pwd = new Option( "p", true,
            "The password for the user this Agent is owned by. If blank you will be prompted for same." );
        pwd.setRequired( false );
        opts.addOption( pwd );

        BasicParser parser = new BasicParser();
        HelpFormatter formatter = new HelpFormatter();
        CommandLine cl = null;
        try
        {
            cl = parser.parse( opts, args );
        }
        catch ( ParseException e )
        {
            formatter.printHelp( "Bad argument(s):", opts );
            System.exit( -1 );
        }
        if ( cl.hasOption( 'h' ) )
        {
            formatter.printHelp( "Help:", opts );
            System.exit( 0 );
        }
        String userName;
        String password;
        boolean localModeFlag = cl.hasOption( "l" );
        if ( !cl.hasOption( 'u' ) )
        {
            PasswordDialog dialog = new PasswordDialog( null, "Username and Password?",
                "Please set the username and password for this session." );
            dialog.setDefaultUserName( agentProps.getLastUserName() );
            dialog.setVisible( true );
            if ( dialog.getUserCanceled() )
            {
                System.out.println( "Username and password required. Exiting." );
                System.exit( -1 );
            }
            userName = dialog.getUserName();
            password = new String( dialog.getPassword() );
        }
        else
        {
            userName = cl.getOptionValue( 'u' );
            password = cl.getOptionValue( 'p' );

        }
        agentProps.setUserName( userName );
        agentProps.setPassword( password );
        agentProps.writeOut();

        if ( localModeFlag )
        {
            agentProps.setLocalMode( localModeFlag ); // command line arg wins out over property settings.
        }

        boolean localMode = agentProps.getLocalMode();
        if ( !localMode )
        {
            try
            {
                System.out.println( "Registering Agent with server (" + agentProps.serverAddress + ")" );
                AgentToServerComm.registerSelfWithServer( agentProps );
            }
            catch ( IOException e )
            {
                int resp = JOptionPane.showConfirmDialog( null,
                    "Could not connect to server, would you like to continue in local mode?",
                    "Cannot connect to server.",
                    JOptionPane.YES_NO_OPTION,
                    JOptionPane.QUESTION_MESSAGE );
                if ( resp == JOptionPane.NO_OPTION )
                {
                    System.out.println( "Exiting..." );
                    System.exit( 0 );
                }
                else
                {
                    localMode = true;
                    agentProps.setLocalMode( localMode );
                }

            }
        }
        System.out.println( "Starting server" );

        startTomcat( agentProps );


    }

    public static void startTomcat( AgentProperties agentProps )
    {
        /**
         * copied from org.apache.catalina.startup.Embedded to facilitate contstruction.
         * <li>Call <code>createEngine()</code> to create an Engine object, and then
         *     call its property setters as desired.</li>
         * <li>Call <code>createHost()</code> to create at least one virtual Host
         *     associated with the newly created Engine, and then call its property
         *     setters as desired.  After you customize this Host, add it to the
         *     corresponding Engine with <code>engine.addChild(host)</code>.</li>
         * <li>Call <code>createContext()</code> to create at least one Context
         *     associated with each newly created Host, and then call its property
         *     setters as desired.  You <strong>SHOULD</strong> create a Context with
         *     a pathname equal to a zero-length string, which will be used to process
         *     all requests not mapped to some other Context.  After you customize
         *     this Context, add it to the corresponding Host with
         *     <code>host.addChild(context)</code>.</li>
         * <li>Call <code>addEngine()</code> to attach this Engine to the set of
         *     defined Engines for this object.</li>
         * <li>Call <code>createConnector()</code> to create at least one TCP/IP
         *     connector, and then call its property setters as desired.</li>
         * <li>Call <code>addConnector()</code> to attach this Connector to the set
         *     of defined Connectors for this object.  The added Connector will use
         *     the most recently added Engine to process its received requests.</li>
         * <li>Repeat the above series of steps as often as required (although there
         *     will typically be only one Engine instance created).</li>
         * <li>Call <code>start()</code> to initiate normal operations of all the
         *     attached components.</li>
         * </ul>
         *
         * See also http://www.vsj.co.uk/java/display.asp?id=319 for a very helpful walkthrough.
         *
         */

        try
        {
            final Embedded tomcat = new Embedded();
            final int port = agentProps.getListenPort();

            File runDir = new File( runningPath );
            File resourcesHome = new File( runDir, "resources" );
            File catalinaHome = new File( resourcesHome, "tomcat" );
            System.out.println( "Setting $CATALINA_HOME and $CATALINA_BASE to:" );
            System.out.println( "\t" + catalinaHome.getCanonicalPath() );
            File conf = new File( catalinaHome, "conf" );
            File tomcatUsers = new File( conf, "tomcat-users.xml" );

            boolean tcUsersExists = tomcatUsers.exists();
            if ( !tcUsersExists )
            {
                System.out.println( "Creating user directory '" + tomcatUsers.getCanonicalPath() + "'." );
                tomcatUsers.mkdirs();
            }

            System.out.println( "Configuring Agent." );
            tomcat.setCatalinaHome( catalinaHome.getCanonicalPath() );
            tomcat.setCatalinaBase( catalinaHome.getCanonicalPath() );
            // create an Engine
            Engine tEngine = tomcat.createEngine();

            MemoryRealm memRealm = new MemoryRealm();
            tEngine.setRealm( memRealm );

            // set Engine properties
            tEngine.setName( "testFireTomcat" );
            tEngine.setDefaultHost( "testFireTomcatHost" );

            // create our host and add it to the engine
            Host host = tomcat.createHost( "testFireTomcatHost", "webapps" );
            tEngine.addChild( host );

            // create the default, fall-through Context and add it to the host
            Context rootCtx = tomcat.createContext( "", "ROOT" );
            host.addChild( rootCtx );

            // create additional context(s) and add them to host.
            Context appCtx = tomcat.createContext( "/manager", "manager" );
            appCtx.setPrivileged( true );
            host.addChild( appCtx );

            //add new Engine to embedded server
            tomcat.addEngine( tEngine );

            // create Connector to listen on the specified port
            Connector httpConnector = tomcat.createConnector( (InetAddress)null, port, false );
            tomcat.addConnector( httpConnector );

            Thread monitor = new Thread()
            {
                public void run()
                {
                    // start 'er up
                    try
                    {
                        System.out.println( String.format( "Starting Agent on port %1$d.", port ) );
                        tomcat.start();
                        System.out
                            .println( String.format(
                                "Agent listening on port %1$d.\nType 'exit' and press enter to shut down instance.\n"
                                    + "Type 'settings' to show the settings dialog.", port ) );

                        char c = '\0';
                        StringBuilder buf = new StringBuilder( 25 );
                        while ( true )
                        {
                            while ( c != '\n' )
                            {
                                c = Character.toChars( System.in.read() )[0];
                                buf.append( c );
                            }
                            String cmd = buf.toString().toLowerCase();
                            if ( cmd.startsWith( "exit" ) )
                            {
                                System.out.println( "Shutdown requested..." );
                                tomcat.stop();
                                break;
                            }
                            else if ( cmd.startsWith( "settings" ) )
                            {
                                new AgentPropertiesDialog().setVisible( true );
                            }
                            else
                            {
                                System.out.println( "Unknown command '" + cmd
                                    + "'. Use 'exit' to shutdown, or 'settings' to show the settings dialog." );
                            }
                            c = '\0';
                            buf.delete( 0, buf.length() );


                        }

                    }
                    catch ( LifecycleException ex )
                    {
                        ex.printStackTrace();
                    }
                    catch ( IOException e )
                    {
                        e.printStackTrace();
                    }
                    System.out.println( "Shutting down...." );
                    System.out.println( "Server termintated, shutting down." );
                    System.out.flush();
                    System.exit( -1 );
                }
            };
            monitor.setDaemon( false );
            monitor.start();

        }
        catch ( Exception e )
        {
            System.out.println( "Could not configure Tomcat instance." );
            System.exit( -1 );
            e.printStackTrace();
        }

    }

}

// eof: Start.java